package com.daffodil.framework.shiro.session;

import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.time.DateUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.SessionValidationScheduler;

import com.daffodil.framework.shiro.realm.UserRealm;
import com.daffodil.framework.shiro.redis.ShiroRedisCacheManager;
import com.daffodil.framework.shiro.service.SysShiroService;
import com.daffodil.system.entity.SysUserOnline;
import com.daffodil.util.spring.SpringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * @author yweijian
 * @date 2021年4月13日
 * @version 1.0
 * @description
 */
@Slf4j
public class OnlineSessionValidationScheduler implements SessionValidationScheduler, Runnable{

	private ScheduledExecutorService service;
	
	private ShiroRedisCacheManager cacheManager;
	
	/**
	 * 会话全局超期时间
	 */
	private long globalSessionTimeout = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;
	/**
	 * 会话最大生存时间
	 */
	private long interval = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;

	private boolean enabled = false;

	private static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authorizationCache";

	public static final String ACTIVE_SESSION_CACHE_NAME = "shiro-activeSessionCache";

	private static final String THREADNAME_PREFIX = "OnlineSessionValidationThread-";

	@Override
	public boolean isEnabled() {
		return this.enabled;
	}

	@Override
	public void enableSessionValidation() {
		if (this.interval > 0l) {
			this.service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {  
				private final AtomicInteger count = new AtomicInteger(1);

				public Thread newThread(Runnable r) {  
					Thread thread = new Thread(r);
					thread.setDaemon(true);  
					thread.setName(THREADNAME_PREFIX + count.getAndIncrement());
					return thread;  
				}  
			});                  
			this.service.scheduleAtFixedRate(this, interval, interval, TimeUnit.MILLISECONDS);
		}
		this.enabled = true;
	}

	@Override
	public void disableSessionValidation() {
		if (this.service != null) {
			this.service.shutdownNow();
		}
		this.enabled = false;
	}

	@Override
	public void run() {
		this.onlineSessionValidation();
	}
	
	private void onlineSessionValidation() {
		log.info("定时任务--清除无效过期会话...");
		try {
			int timeout = (int) this.getGlobalSessionTimeout();
			Date expiredDate = DateUtils.addMilliseconds(new Date(), 0 - timeout);
			SysShiroService shiroService = SpringUtils.getBean(SysShiroService.class);
			List<SysUserOnline> expireds = shiroService.selectOnlineByExpired(expiredDate);
			Cache<Object, Object> activeSessionCache = cacheManager.getCache(ACTIVE_SESSION_CACHE_NAME);
			Cache<Object, Object> authorizationInfoCache = cacheManager.getCache(UserRealm.class.getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX);

			for (SysUserOnline online : expireds) {
				log.info("定时任务--无效过期会话ID={}", online.getId());
				if(activeSessionCache != null) {
					activeSessionCache.remove(online.getId());
				}
				if (authorizationInfoCache != null) {
					authorizationInfoCache.remove(online.getUserId());
				}
				shiroService.deleteOnlineSession(online.getId());
			}
		}catch (Exception e) {
			log.info("定时任务--清除无效过期会话失败", e);
		}
	}

	public ScheduledExecutorService getService() {
		return service;
	}

	public void setService(ScheduledExecutorService service) {
		this.service = service;
	}
	
	public ShiroRedisCacheManager getCacheManager() {
		return cacheManager;
	}

	public void setCacheManager(ShiroRedisCacheManager cacheManager) {
		this.cacheManager = cacheManager;
	}

	public long getGlobalSessionTimeout() {
		return globalSessionTimeout;
	}

	public void setGlobalSessionTimeout(long globalSessionTimeout) {
		this.globalSessionTimeout = globalSessionTimeout;
	}

	public long getInterval() {
		return interval;
	}

	public void setInterval(long interval) {
		this.interval = interval;
	}

	public OnlineSessionValidationScheduler() {
		super();
	}

	public OnlineSessionValidationScheduler(ShiroRedisCacheManager cacheManager, long globalSessionTimeout, long interval) {
		super();
		this.cacheManager = cacheManager;
		this.globalSessionTimeout = globalSessionTimeout;
		this.interval = interval;
	}

}
